package com.cys.markdown.doc.admin.controller;

import com.cys.common.data.RespCodeEnum;
import com.cys.common.pojo.RespEntity;
import com.cys.common.util.base.IpUtil;
import com.cys.common.util.base.StringUtil;
import com.cys.common.util.encrypt.RSAUtil;
import com.cys.markdown.doc.admin.config.KaptchaConfig;
import com.cys.markdown.doc.admin.shiro.ExtAuthenticationToken;
import com.cys.mid.core.system.interfaces.entity.ISysUser;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;

/**
 * 登录控制器
 * Created by caiyushen on 2018/8/23.
 */
@Controller
@RequestMapping("/login")
@Transactional
public class LoginController {

    private Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @Value("${captcha.flag}")
    private Boolean captchaFlag;


    /**
     * 跳转到登录页面【Web】
     */
    @RequestMapping(value = "/toIndex", method = RequestMethod.GET)
    public String toIndex() {
        logger.info("method[toIndex]");
        return "login";
    }

    /**
     * 跳转到会话过期
     */
    @RequestMapping(value = "/toSessionTimeout", method = RequestMethod.GET)
    @ResponseBody
    public RespEntity toSessionTimeout() {
        logger.info("method[toSessionTimeout]");
        return new RespEntity(RespCodeEnum.session_timeout);
    }

    /**
     * 跳转到已授权【Api】
     */
    @RequestMapping(value = "/toAuthorized", method = RequestMethod.GET)
    @ResponseBody
    public RespEntity toAuthorized() {
        logger.info("method[toAuthorized]");
        return new RespEntity(RespCodeEnum.success);
    }

    /**
     * 登录操作
     *
     * @param userName    用户账号
     * @param password    用户密码
     * @param captchaCode 验证码
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public RespEntity login(HttpServletRequest httpServletRequest, String userName, String password, String captchaCode) {
        logger.info("method[login] userName:" + userName + " password:" + password + " captchaCode:" + captchaCode);
        if (captchaFlag) {
            if (!StringUtil.checkObj(captchaCode)) {
                logger.warn("method[login] 验证码不能为空。 captchaCode:" + captchaCode.toUpperCase());
                return new RespEntity(RespCodeEnum.error, "验证码不能为空");
            }
        }
        //判断是否需要图形验证
        if (captchaFlag) {
            //从session中获取验证码
            String captchaId = (String) httpServletRequest.getSession().getAttribute("vrifyCode");
            if (!captchaCode.toUpperCase().equals(captchaId)) {
                logger.warn("method[login] 验证码出错。 captchaId:" + captchaId + " captchaCode:" + captchaCode.toUpperCase());
                return new RespEntity(RespCodeEnum.error, "验证码出错");
            }
        }
        //判断是否需要rsa加密
        //从session中获取加密私钥
        String privateExponent = (String) httpServletRequest.getSession().getAttribute("privateExponent");
        if (!StringUtil.checkObj(privateExponent)) {
            logger.error("method[login] 无法获取私钥。 privateExponent is null.");
            return new RespEntity(RespCodeEnum.warn);
        }
        //获取模hex
        String modulus = (String) httpServletRequest.getSession().getAttribute("modulus");
        //构建私钥
        RSAPrivateKey priKey = RSAUtil.getPrivateKey(modulus, privateExponent);
        try {
            userName = new String(RSAUtil.decryptByPrivateKey(RSAUtil.hexStringToBytes(userName), priKey));
            password = new String(RSAUtil.decryptByPrivateKey(RSAUtil.hexStringToBytes(password), priKey));
        } catch (Exception e) {
            logger.error("method[login] RSA 解密失败 e:" + e.toString());
            logger.error(e.getMessage(), e);
            throw new AuthenticationException("密码错误");
        }

        try {
            Subject currentUser = SecurityUtils.getSubject();
            String ipHost = new IpUtil().getRemoteHost(httpServletRequest);
            ExtAuthenticationToken token = new ExtAuthenticationToken(userName, password, ipHost);
            //会跳到我们自定义的realm中--》shiroRealm
            currentUser.login(token);
            ISysUser sysUser = (ISysUser) currentUser.getPrincipal();
            Session session = currentUser.getSession();//获取Session
            session.setAttribute("sysUserId", sysUser.getSysUserId());//将UserId数据到session里面
            //记录登录日志
//            systemLogService.save(SystemLogTypeEnum.login, "账号【" + userName + "】登录成功。ip:" + ipHost, systemStaff);
        } catch (IncorrectCredentialsException e) {
            //捕捉登录验证异常，防止事务回滚
            logger.warn("method[login] e:" + e.toString());
            return new RespEntity(RespCodeEnum.error, e.getMessage());
        } catch (AuthenticationException e) {
            //捕捉登录验证异常，防止事务回滚
            logger.warn("method[login] e:" + e.toString());
            return new RespEntity(RespCodeEnum.error, e.getMessage());
        }
        return new RespEntity(RespCodeEnum.success);
    }


    /**
     * 退出登录【Api】
     */
    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    @ResponseBody
    public RespEntity logout() {
        //shiro管理的session
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        session.removeAttribute("userId");
        //shiro销毁登录
        currentUser.logout();
        return new RespEntity(RespCodeEnum.success);
    }

    /**
     * 获取图片验证码
     */
    @RequestMapping("/defaultKaptcha")
    public void defaultKaptcha(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        byte[] captchaChallengeAsJpeg = null;
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        try {
            DefaultKaptcha defaultKaptcha = new KaptchaConfig().getDefaultKaptcha();
            //生产验证码字符串并保存到session中
            String createText = defaultKaptcha.createText();
            httpServletRequest.getSession().setAttribute("vrifyCode", createText);
            //使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中
            BufferedImage challenge = defaultKaptcha.createImage(createText);
            ImageIO.write(challenge, "jpg", jpegOutputStream);
        } catch (IllegalArgumentException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        //定义response输出类型为image/jpeg类型，使用response输出流输出图片的byte数组
        captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
        httpServletResponse.setHeader("Cache-Control", "no-store");
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        httpServletResponse.setContentType("image/jpeg");
        ServletOutputStream responseOutputStream = httpServletResponse.getOutputStream();
        responseOutputStream.write(captchaChallengeAsJpeg);
        responseOutputStream.flush();
        responseOutputStream.close();
    }

    /**
     * 获取RSA算法公钥以及随机数 并 将存储私钥到Session里面
     */
    @RequestMapping(value = "/getRSAKey", method = RequestMethod.GET)
    @ResponseBody
    public RespEntity getRSAKey(HttpServletRequest httpServletRequest) throws Exception {
        Map result = new HashMap();
        KeyPair keyPair = RSAUtil.genRSAKeyPair();
        //生成公钥和私钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        //公钥指数hex
        String publicExponent = publicKey.getPublicExponent().toString(16);
        //私钥指数hex
        String privateExponent = privateKey.getPrivateExponent().toString(16);
        //模hex
        String modulus = publicKey.getModulus().toString(16);

        logger.debug("公钥: " + modulus);
        logger.debug("publicExponent: " + publicExponent);
        logger.debug("私钥: " + privateExponent);

        //公钥以及加密位数 返回到前端
        result.put("modulus", modulus);
        result.put("publicExponent", publicExponent);

        //存储在session里面
        httpServletRequest.getSession().setAttribute("modulus", modulus);
        httpServletRequest.getSession().setAttribute("publicExponent", publicExponent);
        httpServletRequest.getSession().setAttribute("privateExponent", privateExponent);

        return new RespEntity(RespCodeEnum.success, result);
    }


}
